    // Copyright (C) 2005  Davis E. King (davis@dlib.net)
    // License: Boost Software License   See LICENSE.txt for the full license.
    #ifndef DLIB_LZP_BUFFER_KERNEl_1_
    #define DLIB_LZP_BUFFER_KERNEl_1_

    #include "../algs.h"
    #include "lzp_buffer_kernel_abstract.h"

    namespace dlib
    {

        template <
            typename sbuf
            >
        class lzp_buffer_kernel_1
        {
            /*!
                REQUIREMENTS ON sbuf
                    sbuf is an implementation of sliding_buffer/sliding_buffer_kernel_abstract.h
                    T == unsigned char

                INITIAL VALUE
                    - buffer.size() == the size as defined by the constructor
                    - table_size == the number of elements in the table array
                    - for all i: buffer[i] == 0
                    - for all i: table[i] == buffer.size()

                CONVENTION
                    - table_size == the number of elements in the table array
                    - size() == buffer.size()
                    - operator[](i) == buffer[i]

                    - if (table[hash()] != buffer.size()) then
                        - buffer.get_element_index(table[hash()]) == the index we will
                          predict for the current context
                    - else
                        - there is no prediction for the current context

                    - last_element == buffer.size()-1

                    This is LZP with just an order-3 model without context confirmation.

            !*/

        public:

            explicit lzp_buffer_kernel_1 (
                unsigned long buffer_size
            );

            virtual ~lzp_buffer_kernel_1 (
            );

            void clear(
            );

            inline void add (
                unsigned char symbol
            );

            inline unsigned long predict_match (
                unsigned long& index
            );

            inline size_t size (
            ) const;

            inline unsigned char operator[] (
                unsigned long index
            ) const;

        private:

            inline unsigned long hash (
            ) const
            /*!
                ensures
                    - returns a hash computed from the current context.  This hash
                      is always in the range for table.
            !*/
            {
                unsigned long temp = buffer[0];
                temp <<= 16;
                unsigned long temp2 = buffer[1];
                temp2 <<= 8;
                unsigned long temp3 = buffer[2];
                temp = temp|temp2|temp3;

                temp = ((temp>>11)^temp)&0xFFFF;

                return temp;
            }

            sbuf buffer;
            const unsigned long table_size;
            unsigned long* const table;
            unsigned long last_element;

            // restricted functions
            lzp_buffer_kernel_1(const lzp_buffer_kernel_1<sbuf>&);        // copy constructor
            lzp_buffer_kernel_1<sbuf>& operator=(const lzp_buffer_kernel_1<sbuf>&);    // assignment operator

        };

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // member function definitions
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        template <
            typename sbuf
            >
        lzp_buffer_kernel_1<sbuf>::
        lzp_buffer_kernel_1 (
            unsigned long buffer_size
        ) :
            table_size(65536),
            table(new unsigned long[table_size])
        {
            buffer.set_size(buffer_size);

            for (unsigned long i = 0; i < buffer.size(); ++i)
                buffer[i] = 0;

            for (unsigned long i = 0; i < table_size; ++i)
                table[i] = buffer.size();

            last_element = buffer.size()-1;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename sbuf
            >
        lzp_buffer_kernel_1<sbuf>::
        ~lzp_buffer_kernel_1 (
        )
        {
            delete [] table;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename sbuf
            >
        void lzp_buffer_kernel_1<sbuf>::
        clear(
        )
        {
            for (unsigned long i = 0; i < buffer.size(); ++i)
                buffer[i] = 0;

            for (unsigned long i = 0; i < table_size; ++i)
                table[i] = buffer.size();
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename sbuf
            >
        void lzp_buffer_kernel_1<sbuf>::
        add (
            unsigned char symbol
        )
        {
            buffer.rotate_left(1);
            buffer[0] = symbol;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename sbuf
            >
        unsigned long lzp_buffer_kernel_1<sbuf>::
        predict_match (
            unsigned long& index
        )
        {
            const unsigned long i = hash();

            if (table[i] != buffer.size())
            {
                index = buffer.get_element_index(table[i]);

                if (index > 20)
                {
                    // update the prediction for this context
                    table[i] = buffer.get_element_id(last_element);
                }
                return 3;
            }
            else
            {
                // update the prediction for this context
                table[i] = buffer.get_element_id(last_element);
                return 0;
            }
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename sbuf
            >
        size_t lzp_buffer_kernel_1<sbuf>::
        size (
        ) const
        {
            return buffer.size();
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename sbuf
            >
        unsigned char lzp_buffer_kernel_1<sbuf>::
        operator[] (
            unsigned long index
        ) const
        {
            return buffer[index];
        }

    // ----------------------------------------------------------------------------------------

    }

    #endif // DLIB_LZP_BUFFER_KERNEl_1_